using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;

namespace PDO.Utilities
{
    public class FileSystemItem
    {
        private string _path;

        public FileSystemItem(string path)
        {
            AbsolutePath = path;
        }

        public static FileSystemItem GetSpecialFolder(Environment.SpecialFolder specialFolder)
        {
            return new FileSystemItem(Environment.GetFolderPath(specialFolder));
        }

        public FileSystemItem Parent
        {
            get
            {
                System.IO.DirectoryInfo info;
                info = System.IO.Directory.GetParent(_path);
                if (info == null)
                {
                    return null;
                }
                else
                {
                    return new FileSystemItem(info.ToString());
                }
            }
        }


        public bool Exists
        {
            get
            {
                return (System.IO.Directory.Exists(_path) || System.IO.File.Exists(_path));
            }
        }


        /// <summary>
        /// Returns the absolute path, and guaranties that it never ends with a "\"
        /// </summary>
        public string AbsolutePath
        {
            get
            {
                if (_path.EndsWith(Path.DirectorySeparatorChar.ToString()))
                {
                    return _path.Substring(0, _path.Length - 1);
                }
                else
                {
                    return _path;
                }
            }
            set
            {
                _path = value.Replace("\\\\", Path.DirectorySeparatorChar.ToString());
            }
        }


        public bool Directory
        {
            get
            {
                return System.IO.Directory.Exists(_path);
            }
        }


        public int Count
        {
            get
            {
                return System.IO.Directory.GetFileSystemEntries(_path).GetLength(0);
            }
        }

        public string FileType
        {
            get
            {
                string s;
                if (Directory)
                {
                    return "";
                }
                else
                {
                    s = System.IO.Path.GetExtension(_path);
                    return s.Substring(1, s.Length - 1);
                }
            }
            set
            {
                if (!Directory)
                {
                    System.IO.Path.ChangeExtension(_path, (value.Substring(0, 1) == ".") ? value : "." + value);
                }
            }
        }

        public Hashtable NamedChild
        {
            get
            {
                string[] s;
                Hashtable items;
                s = System.IO.Directory.GetFileSystemEntries(_path);
                items = new Hashtable();
                for (int i = s.GetLowerBound(0); i <= s.GetUpperBound(0); i++)
                {
                    items.Add(s[i], new FileSystemItem(System.IO.Path.Combine(_path, s[i])));
                }
                return items;
            }
        }

        public List<FileSystemItem> Children
        {
            get
            {
                string[] s;
                List<FileSystemItem> items;
                s = System.IO.Directory.GetFileSystemEntries(_path);
                items = new List<FileSystemItem>();
                for (int i = s.GetLowerBound(0); i <= s.GetUpperBound(0); i++)
                {
                    items.Add(new FileSystemItem(System.IO.Path.Combine(_path, s[i])));
                }
                return items;
            }
        }

        public FileSystemItem Child(string name)
        {
            if (Directory)
            {
                if (_path.EndsWith("\\"))
                {
                    return new FileSystemItem(_path + name);
                }
                else
                {
                    return new FileSystemItem(_path + "\\" + name);
                }
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Return the full name of the file.
        /// </summary>
        public string FullName
        {
            get
            {
                return System.IO.Path.GetFileName(_path);
            }
        }

        /// <summary>
        /// If a file is represented, the file name will be returned without extension.
        /// If a directory is represented, a blank will be returned.
        /// </summary>
        public string Name
        {
            get
            {
                if (Directory)
                {
                    return System.IO.Path.GetFileName(_path);
                }
                else
                {
                    return System.IO.Path.GetFileNameWithoutExtension(_path);
                }
            }
        }


        private bool GetFileAttribute(System.IO.FileAttributes newAttr)
        {
            System.IO.FileAttributes attr;
            attr = System.IO.File.GetAttributes(_path);
            return ((attr & newAttr) > 0);
        }

        private void SetFileAttribute(System.IO.FileAttributes newAttr, bool value)
        {
            System.IO.FileAttributes attr;
            attr = System.IO.File.GetAttributes(_path);
            if (value)
            {
                System.IO.File.SetAttributes(_path, attr | newAttr);
            }
            else
            {
                System.IO.File.SetAttributes(_path, attr & ~newAttr);
            }
        }

        public bool Locked
        {
            get
            {
                return GetFileAttribute(System.IO.FileAttributes.ReadOnly);
            }
            set
            {
                SetFileAttribute(System.IO.FileAttributes.ReadOnly, value);
            }
        }

        public bool Visible
        {
            get
            {
                return !GetFileAttribute(System.IO.FileAttributes.Hidden);
            }
            set
            {
                SetFileAttribute(System.IO.FileAttributes.Hidden, !value);
            }
        }



        public void CreateAsFolder()
        {
            if (!Exists)
            {
                System.IO.Directory.CreateDirectory(_path);
            }
        }


        public void Copy(FileSystemItem destination, bool overwrite)
        {
            System.IO.File.Copy(_path, destination.AbsolutePath, overwrite);
        }

        public void Copy(FileSystemItem destination)
        {
            System.IO.File.Copy(_path, destination.AbsolutePath);
        }


        public void Delete()
        {
            if (Exists)
            {
                if (!Locked)
                {
                    if (Directory)
                    {
                        System.IO.Directory.Delete(_path, true);
                    }
                    else
                    {
                        System.IO.File.Delete(_path);
                    }
                }
            }
        }

        public void Launch()
        {
            System.Diagnostics.Process p = new System.Diagnostics.Process();
            p.StartInfo.FileName = AbsolutePath;
            p.Start();
        }

        public override string ToString()
        {
            return System.IO.Path.GetFullPath(_path);
        }

        /// <summary>
        /// Returns free space available on the given file path
        /// </summary>
        /// <param name="path">path to check</param>
        /// <returns>Bytes free</returns>
        public static long GetFreeDiskSpace(string path)
        {
            DriveInfo di = new DriveInfo(System.IO.Directory.GetDirectoryRoot(path));
            return di.AvailableFreeSpace;
        }

        /// <summary>
        /// Returns total space available on the given file path
        /// </summary>
        /// <param name="path">path to check</param>
        /// <returns>bytes total</returns>
        public static long GetTotalDiskSpace(string path)
        {
            DriveInfo di = new DriveInfo(System.IO.Directory.GetDirectoryRoot(path));
            return di.TotalSize;
        }

        /// <summary>
        /// Combines given paths
        /// </summary>
        /// <param name="path1"></param>
        /// <param name="path2"></param>
        /// <returns></returns>
        public static string CombinePaths(params object[] paths)
        {
            StringBuilder fullPath = new StringBuilder();
            for (int i = 0; i <= paths.GetLength(0) - 1; i++)
            {
                fullPath.Append(paths[i]);
                if (i != paths.GetLength(0) - 1)//if not the last path
                {
                    fullPath.Append(Path.DirectorySeparatorChar);
                }
            }
            return fullPath.ToString();
        }

        /// <summary>
        /// Gets the size of given file in bytes
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        public static long GetFileSize(string filename)
        {
            FileInfo file = new FileInfo(filename);
            return file.Length;
        }

        /// <summary>
        /// Generates a dummy file with the given size in bytes
        /// </summary>
        /// <param name="filename"></param>
        /// <param name="filesize"></param>
        public static void GenerateFile(string filename, long filesizeBytes)
        {
            StreamWriter oWriter = new StreamWriter(filename);
            StringBuilder oData = new StringBuilder();

            for (long i = 1; i <= filesizeBytes; i++)
            {
                oData.Append("0");
                if (i % 1024 == 0)//write in chunks of 1kb - otherwise StringBuilder will run out of memory
                {
                    oWriter.WriteLine(oData.ToString());
                    oData.Remove(0, oData.Length - 1);//clear StringBuilder
                }
            }
            oWriter.Close();
        }

        /// <summary>
        /// Returns all the folders in the given path except the drive
        /// </summary>
        /// <param name="fullPath">full path of the directory</param>
        /// <returns>List of folders</returns>
        public static List<string> GetFolders(string fullPath)
        {
            List<string> folders = new List<string>(fullPath.Split(Path.DirectorySeparatorChar));
            return folders.GetRange(1, folders.Count - 1);
        }



        /// <summary>
        /// Creates the given folder hierarchy - if folder does not exist, it will be created
        /// Otherwise existing folders will not be overwritten
        /// </summary>
        /// <param name="fullPath"></param>
        public static void CreateFolderHierarchy(string fullPath)
        {
            string drive = System.IO.Directory.GetDirectoryRoot(fullPath);
            string tempPath = drive;
            foreach (string folder in GetFolders(fullPath))
            {
                tempPath = Path.Combine(tempPath, folder);
                if (!System.IO.Directory.Exists(tempPath))
                {
                    System.IO.Directory.CreateDirectory(tempPath);
                }
            }
        }


        /// <summary>
        /// Performs XCopy operation on destination folder
        /// </summary>
        /// <param name="source">source path</param>
        /// <param name="destination">destination path inside which xcopy will be done</param>
        public static void DoXCopy(string source, string destination)
        {
            /*XCopy can be done using .NET IO classes but it will take a huge number of IO operations and
             * a large number of string operations for paths and all - that is why use of system XCopy
             * command is going to be faster and cleaner*/
            ProcessStartInfo processInfo = new ProcessStartInfo("xcopy");
            processInfo.Arguments = "/E " + source + " " + destination;
            processInfo.CreateNoWindow = true;
            processInfo.RedirectStandardInput = false;
            processInfo.RedirectStandardOutput = false;
            processInfo.UseShellExecute = false;
            processInfo.WindowStyle = ProcessWindowStyle.Hidden;

            Process xcopyProcess = new Process();
            xcopyProcess.StartInfo = processInfo;
            xcopyProcess.Start();
            xcopyProcess.WaitForExit();
        }

    }
}
